package behaviorTree

import (
	"gitee.com/kingzyt/common/log"
)

type BehaviorTree struct {
	name string
	root INode
}

func NewBehaviorTree(name string) *BehaviorTree {
	return &BehaviorTree{name: name}
}

func (self *BehaviorTree) SetRoot(root INode) {
	if root == nil {
		panic("root is nil")
	}

	root.CheckValidation()

	self.root = root
}

func (self *BehaviorTree) Update(data IRuntimeBTData, dt int64) {
	if data.IsPaused() {
		return
	}

	data.CheckAbort()

	stack := data.GetRuntimeNodeStack()

	traceTree := func(stateIn NodeState) (oneTraceEnd bool, rltState NodeState) {
		// in: node, stateIn, localDataIn
		node, _, localDataIn := stack.GetTopNode()
		for node != nil {
			if stateIn <= INVALID || stateIn >= NodeStateMax {
				log.Error(node.GetName(), "state(%d) is not supported !", stateIn)
				return false, FAILURE
			}
			toNode, state, localData := node.Enter(data, stateIn, localDataIn)

			//log.Info(data.GetName(), log.Trace, "top state:%s", state)

			stack.SetTopNodeData(state, localData)
			if toNode != nil {
				stack.PushNode(toNode, nil)
				node = toNode
				stateIn = BEGIN
				localDataIn = nil
			} else {
				if state == RUNNING {
					return false, RUNNING
				}
				stack.PopNode()
				node, _, localDataIn = stack.GetTopNode()
				stateIn = state
			}
		}

		log.Info(data.GetName(), log.Trace, "one trace end, cur state:%s", stateIn)

		return true, stateIn
	}

	if stack.Len() > 0 {
		node, state, localData := stack.GetTopNode()
		if state == RUNNING {
			state, localData = node.RunningCheck(data, localData)
			stack.SetTopNodeData(state, localData)
		}

		if state != RUNNING {
			data.ClearRunningT()

			stack.PopNode()
			end, rltState := traceTree(state)
			if end {
				_ = rltState
			}
		} else {
			data.AddRunningT(dt)
			if data.GetRunningT() > data.GetRunningTimeoutT(node.GetName(), node.GetType()) {
				if stack.Len() > 0 {
					stack.Clear()
				}
				data.ClearRunningT()
				data.Abort(node.GetName() + " running timeout")
				return
			}
		}
	} else {
		stack.PushNode(self.root, nil)
		end, rltState := traceTree(BEGIN)
		if end {
			_ = rltState
		}
	}
}
